4.7 Arbeiten mit Objektreferenzen  
4.7.1 Prüfen auf Initialisierung  
Der Zustand einer Objektvariablen, die nicht initialisiert ist, wird durch Nothing beschrieben. Ist man sich über den Zustand einer Objektvariablen im Unklaren, kann dieser mit der Funktion IsNothing überprüft werden.
| Dim obj As MyOwnClass
If IsNothing(obj) Then
Console.WriteLine("Kein gültiger Verweis.")
End If
|
Der Aufruf von IsNothing liefert True als Rückgabe, falls die Objektvariable nicht initialisiert ist. Genau dies ist auch das Ergebnis des obigen Codefragments, da vor der Überprüfung keine Initialisierung mit dem New-Operator erfolgt.
Eine weitere Variante zur Prüfung auf Initialisierung wäre die folgende:
| If Not (obj Is Nothing) Then
|
| Console.WriteLine("Das Objekt ist initialisiert")
|
| End If
|
Mit dem Erscheinen von Visual Basic 2005 wurde den Entwicklern zudem noch eine dritte Möglichkeit mit dem Schlüsselwort IsNot bereitgestellt:
| If obj IsNot Nothing Then
|
| ...
|
| End If
|
4.7.2 Mehrere Referenzen auf ein Objekt  
Es kommt immer wieder vor, dass mehrere Referenzen auf das gleiche Objekt zeigen. Betrachten Sie dazu das folgende Codefragment:
| Dim object1 As New ClassA
|
| Dim object2 As ClassA
|
| object2 = object1
|
In der ersten Zeile wird eine Variable des Typs ClassA deklariert und die Klasse instanziiert. Die Variable object2 ist ebenfalls vom Typ ClassA, jedoch nur deklariert und nicht initialisiert. In der dritten Anweisung wird die Referenz auf das Objekt object1 der Variablen object2 zugewiesen. Nach der Ausführung der dritten Codezeile liegt zwar nur ein Objekt vor, dieses kann allerdings über zwei Referenzen angesprochen werden.
Weisen Sie einem Feld des Objekts über eine der beiden Referenzen einen Wert zu, beispielsweise mit
könnten Sie folglich auch mit der zweiten Objektreferenz den Inhalt der Eigenschaft auslesen:
| Console.WriteLine(object2.MyProp)
|
An der Konsole wird 4711 angezeigt, da die Variable object2 auf dasselbe Objekt verweist wie die Variable object1. Wird ein Objekt mehrfach referenziert, spielt es demnach keine Rolle, über welche Referenz der Eigenschaft ein Wert zugewiesen bzw. ein Feld ausgelesen wird – die Operation wird auf demselben konkreten Objekt ausgeführt.
Eine Mehrfachreferenzierung hat auch noch weitere Konsequenzen: Geben Sie eine der Referenzen mit Nothing frei, wird das Objekt nicht zerstört, weil eine andere, gültige Referenz immer noch das Weiterleben des Objekts garantiert.
| Dim object1 As New ClassA
|
| Dim obejct2 As ClassA
|
| object2 = object1
|
| object1.MyProp = 4711
|
| object1 = Nothing
|
| Console.WriteLine(object2.MyProp)
|
An der Konsole würde in diesem Fall immer noch der Inhalt der Eigenschaft MyProp ausgegeben. Erst mit der Freigabe der letzten Referenz auf eine Klasseninstanz wird das Objekt aufgegeben und kann zu einem späteren Zeitpunkt von der Speicherbereinigung erfasst werden.
4.7.3 Typfeststellung einer Objektreferenz  
Sie werden sehr häufig in die Situation kommen, den Typ einer Objektreferenz zu ermitteln. Die sprachspezifische Funktion TypeName von Visual Basic ist dazu geeignet. Wie diese benutzt wird, demonstriert das folgende Beispiel:
| If TypeName(myObj) = "ResultSet" Then
|
| Console.WriteLine("Volltreffer")
|
| End If
|
Hinsichtlich der Performance sind Stringvergleiche allerdings eine schlechte Wahl. Leistungsmäßig besser wird der Einsatz des TypeOf ... Is-Operators ausfallen. Der Operator liefert als Rückgabewert True, wenn der Typ des unter Objektname angegebenen Objekts der Angabe unter Typname entspricht oder von diesem abgeleitet ist.
| If TypeOf myObj Is ClassA Then
|
| Console.WriteLine("Es handelt sich um ein ClassA-Objekt.")
|
| End If
|
TypeOf ... Is kann nur auf Referenztypen eingesetzt werden und ist daher ungeeignet, um festzustellen, ob der zugrunde liegende Typ einer Variablen beispielsweise Integer oder Long ist, die beide bekanntlich Wertetypen beschreiben. Der folgende Code ist daher syntaktisch nicht korrekt:
| ' Fehlerhafte Typüberprüfung
|
| Dim intVar As Integer
|
| If TypeOf intVar Is Integer Then
|
| Console.WriteLine("Typ = Integer")
|
| End If
|
Wie kann man aber dieses Dilemma vermeiden, wenn der Datentyp eines Wertetyps festgestellt werden muss?
Ein kleiner Trick ist notwendig, um die Laufzeitumgebung zu überlisten. Dazu muss man wissen, dass die Laufzeitumgebung in der Lage ist, einen Wertetypen im Bedarfsfall in einen Referenztypen umzuwandeln. Dieser Vorgang, dessen genaue Erklärung auf einen späteren Zeitpunkt verschoben wird, wird als Boxing bezeichnet. Sehen Sie sich dazu den folgenden Code an:
| Sub Main()
|
| Dim intVar As Integer
|
| GetVariableType(intVar)
|
| Console.ReadLine()
|
| End Sub
|
| Sub GetVariableType(ByVal testObj As Object)
|
| If TypeOf testObj Is Object Then
|
| Console.WriteLine("Typ = Object")
|
| End If
|
| If TypeOf testObj Is Integer Then
|
| Console.WriteLine("Typ = Integer")
|
| End If
|
| End Sub
|
In Main wird die Variable intVar vom Typ Integer deklariert. Dabei handelt es sich um einen Wertetyp, der mit TypeOf ... Is nicht direkt untersucht werden kann, weil dieser Operator nur mit Referenztypen arbeitet. Der Trick steckt in der Definition der Methode GetVariableType, die ein Argument vom Typ Object erwartet. Da alle .NET-Klassen aus diesem Typ abgeleitet sind (sogar die nativen Datentypen), akzeptiert die Laufzeitumgebung die Übergabe eines Integers. Object selbst ist ein Referenztyp, deshalb muss der Wertetyp Integer von der Common Language Runtime implizit als Referenztyp betrachtet werden. Die damit verbundene Umwandlung ist als das weiter oben erwähnte Boxing bekannt.
Der Aufruf der Methode GetVariableType verursacht keinen Fehler und wird als Resultat zwei Ausgaben an der Konsole nach sich ziehen:
| Typ = Object
|
| Typ = Integer
|
Damit ist erwiesen, dass ein Wertetyp in den Referenztyp Object geboxt werden kann. Mit dieser Technik, den Parameter einer Methode typallgemein zu deklarieren und einen abgeleiteten Typ als Argument zu übergeben, eröffnen sich weitere programmiertechnische Möglichkeiten, die auch im weiteren Verlauf dieses Buches immer wieder angewendet werden.
Der Sonderfall: Ein String-Vergleich
Es gibt einen Sonderfall, der aus dem Rahmen unserer bisherigen Ausnahmen fällt und deshalb auch einer gesonderten Betrachtung unterzogen werden muss: Zeichenfolgen vom Datentyp String. Das zu verdeutlichen ist die Aufgabe des folgenden Beispielprogramms.
| ' ----------------------------------------------------------
|
| ' Beispiel: ...\Kapitel 4\StringVergleich
|
| ' ----------------------------------------------------------
|
| Module Module1
|
| Sub Main()
|
| Dim str1 As String = "Hallo Welt"
|
| Dim str2 As String = "Hallo Welt"
|
| If str1 Is str2 Then
|
| Console.WriteLine("Beide Strings sind gleich")
|
| Else
|
| Console.WriteLine("Die Strings sind ungleich")
|
| End If
|
| Console.ReadLine()
|
| End Sub
|
| End Module
|
Es sind zwei Objektvariablen vom Typ String deklariert, und demnach sollte erwartungsgemäß in den Else-Zweig der bedingten Anweisung gesprungen werden. Dem ist aber nicht so, denn tatsächlich wird an der Konsole
| Beide Strings sind gleich
|
ausgegeben. Wie kann dieses Phänomen erklärt werden?
Es ist zunächst einmal festzuhalten, dass der Typ String ein Referenztyp ist. Folglich ist der Einsatz des Is-Operators gerechtfertigt. Stringvariablen unterscheiden sich aber von den Variablen anderer Typen: Sie können nicht verändert werden. Die folgenden beiden Codezeilen sollen das erklären:
| Dim str1 As String = "Hallo Welt"
|
| str1 = "Hallo Welt und guten Morgen"
|
In der ersten Zeile wird eine Variable vom Typ String deklariert und initialisiert, in der zweiten Zeile wird ihr ein neuer Inhalt zugewiesen. Tatsächlich wird aber der alte Inhalt nicht überschrieben, sondern eine neue Speicheradresse reserviert, der Zeiger von der alten Adresse mit dem Inhalt »Hallo Welt« auf die neue Adresse umgebogen und der neue Inhalt »Hallo Welt und guten Morgen« hineingeschrieben.
Definitiv arbeitet dieser Code also tatsächlich mit zwei unterschiedlichen Speicheradressen, auch wenn wir als Anwender davon nichts bemerken. Dieses Verhalten scheint nicht sehr effizient zu sein, denn Operation kostet zweifelsfrei Systemleistung. Dennoch muss es einen Vorteil geben. Dieser kommt zum Tragen, wenn mehrere Stringvariablen denselben Inhalt haben, denn unveränderliche Strings sind auch für die gemeinsame Nutzung eingerichtet. Das ist im obigen Beispiel StringVergleich der Fall und wird durch das Ausgabeergebnis an der Konsole auch bewiesen: str1 und str2 verweisen auf dieselbe Speicheradresse, weil sie denselben Inhalt beschreiben.
4.7.4 Zusammenfassung  
|
Nichtinitialisierte Objektreferenzen werden durch Nothing beschrieben. Die Prüfung, ob eine Referenz initialisiert ist, kann mit der Funktion IsNothing unter der Übergabe der zu prüfenden Referenz erfolgen. |
|
Um den Typ eines Objekts festzustellen, bietet sich die Funktion TypeName an, die den Klassennamen als Zeichenfolge zurückliefert. |
|
Mit TypeOf ... Is lässt sich der Typ einer Klasse in einer bedingten Anweisung überprüfen. Wertetypen sind von dieser Überprüfung ausgeschlossen. Der Rückgabewert ist vom Typ Boolean. |
|
Eine weitere Variante des Refernzvergleichs bietet der Operator Is, der auf zwei Operanden vom Typ einer Referenz arbeitet. |
|
Für Variablen vom Typ String gelten besondere Bedingungen. Daher ist der Referenzvergleich von String-Objekten ungeeignet. |
|